﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "GbmpCommandBase.h"
#include "GbmpUiPlatformCommandIds.h"
#include "GbmpUiPlatformCommandRegister.h"
#include "CmdStatusUtils.h"
#include "IModelView.h"
#include "UiCommonDialog.h"
#include "ActionDrawStraightElementInput.h"
#include "IUiView.h"
#include "ICanvas.h"
#include "ICamera.h"
#include "IDocument.h"
#include "IUserTransaction.h"
#include "GbmpModelViewUtil.h"
#include "Vector3dUtils.h"
#include "IGraphicsElementShape.h"
#include "IGraphicsNodeGroup.h"
#include "ITemporaryDimensionContext.h"
#include "ITemporaryDimensionManager.h"
#include "IGbmpSectionView.h"
#include "ISectionViewSymbolNew.h"
#include "IUiDocument.h"

#include "IGraphicsNodeReference.h"
#include "GbmpGraphicsNodeUtils.h"
#include "IHighlights.h"
#include "LevelUtils.h"
#include "UiDocumentViewUtils.h"
#include "ISelection.h"
#include "IPickPointAction.h"
#include "IGraphicsLine.h"
#include "ISteppedSectionPlane.h"
#include "ILine3d.h"
#include "AlgorithmMinimalDistance.h"
#include "IGbmpSteppedViewExternal.h"
#include "Coordinate3d.h"
#include "ICompositeSectionFactory.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;


class CreateStairLineAction : public gcmp::ActionBase
{
    enum ActionStatus
    {
        PICK_DIRECTION_BEGIN = 0,
        PICK_DIRECTION_END,
        PICK_START_POINT,
        PICK_ITEM_POINT
    };
private:
    ActionStatus  m_actionStatus = PICK_DIRECTION_BEGIN;
    std::vector<gcmp::Vector3d> m_pickedPoints;
    Vector3d m_ptTemp;
    SteppedLineStruct m_stStairLine;
    Vector3d m_vectorVertical;  //垂直方向
    Vector3d m_vectorView;
    const IModelView* m_pModelView;
    Vector3d m_ptFirstEnd;
    bool m_bUserCancel;
    
public:
    CreateStairLineAction()
    {
        SetCursorType(CursorType::CrossHairCursor);
    }
    ~CreateStairLineAction(void)
    {

    }
    virtual void InitAction(IUiView* pCurrentView) override
    {
        startPickDirBeginAction();
        m_vectorView = -pCurrentView->GetCanvas()->GetCamera()->GetDirection();
        m_pModelView = pCurrentView->GetModelView();
    }
    virtual void OnChildActionFinished(gcmp::IUiView* pCurrentView, const gcmp::ActionOutput& childActionReturnParam) override
    {
        DBG_WARN_AND_RETURN_VOID_UNLESS(pCurrentView, L"pCurrentView不能为空",L"GDMPLab",L"2024-03-30");
        
        switch (m_actionStatus)
        {
        case ActionStatus::PICK_DIRECTION_BEGIN:     
            processFirstStartStatus();
        break;
        case ActionStatus::PICK_DIRECTION_END:
            processFirstEndStatus();
        break;
        case ActionStatus::PICK_ITEM_POINT:
            processItemPointStatus();
        break;
        default:
            break;
        }
        UpdateView();
    }
    void startPickDirBeginAction()
    {
        OwnerPtr<IPickPointAction> opAction = IPickPointAction::Create(PickPointExchangeData(&m_ptTemp, &m_bUserCancel, &m_pickedPoints, nullptr, nullptr,
            PickPointExchangeData::SnapPlaneType::WorkPlane, false, PickPointExchangeData::PickPointType::TwoDimensionPoint));
        m_actionStatus = ActionStatus::PICK_DIRECTION_BEGIN;
        StartChildAction(TransferOwnership(opAction));
    }
    void OnDirEndMoveCallBack(gcmp::IUiView*, const gcmp::Vector3d& pos, const gcmp::Vector3d& mousePt)
    {
        CleanupTempGraphicsShape();
        OwnerPtr<IGraphicsElementShape> opGrep = IGraphicsElementShape::Create(GraphicsRenderLayer::TransientObject);
        opGrep->AddChild(IGraphicsLine::Create(m_stStairLine.StartPoint, pos));
        DrawTempGraphicsElementShape(TransferOwnership(opGrep));
    }
    void startPickDirEndAction()
    {
        OwnerPtr<IPickPointAction> opAction = IPickPointAction::Create(PickPointExchangeData(&m_ptTemp, &m_bUserCancel, &m_pickedPoints, nullptr, nullptr,
            PickPointExchangeData::SnapPlaneType::WorkPlane, false, PickPointExchangeData::PickPointType::TwoDimensionPoint),
            opAction->CreateMoveCallback(&CreateStairLineAction::OnDirEndMoveCallBack, this));
        m_actionStatus = ActionStatus::PICK_DIRECTION_END;
        StartChildAction(TransferOwnership(opAction));        
    }

    static std::list<Vector3d> StairLineToPoints(const SteppedLineStruct& stStairLine, const Vector3d& viewDirect)
    {
        Vector3d dirLenght = stStairLine.SectionDirection;
        Vector3d workPlaneViewDir = viewDirect;
        Vector3d dirHeight = Vector3dUtils::RotateVectorAroundAxis(dirLenght, workPlaneViewDir, Constants::MATH_PI_2);

        std::list<Vector3d> arrLinePoints;
        std::list<Vector3d> arrDeepLinePoints;

        Vector3d ptLineStart = stStairLine.StartPoint;
        Vector3d ptLineEnd  = stStairLine.StartPoint;
        Vector3d ptDeepLineStart ;
        Vector3d ptDeepLineEnd;

        for (auto& item : stStairLine.ArrItem)
        {
            ptLineStart = ptLineEnd + dirHeight * item.Height;
            ptLineEnd = ptLineStart + dirLenght * item.Length;
            ptDeepLineStart = ptLineStart + dirHeight * item.Deep;
            ptDeepLineEnd = ptLineEnd + dirHeight * item.Deep;

            arrLinePoints.emplace_back(ptLineStart);
            arrLinePoints.emplace_back(ptLineEnd);
            arrDeepLinePoints.emplace_back(ptDeepLineStart);
            arrDeepLinePoints.emplace_back(ptDeepLineEnd);
        }
        arrDeepLinePoints.reverse();
        arrLinePoints.insert(arrLinePoints.end(), arrDeepLinePoints.begin(), arrDeepLinePoints.end());
        return arrLinePoints;
    }
    
    void OnItemPointMoveCallBack(gcmp::IUiView*, const gcmp::Vector3d& pos, const gcmp::Vector3d& mousePt)
    {
        OwnerPtr<IGraphicsElementShape> opGrep = IGraphicsElementShape::Create(GraphicsRenderLayer::TransientObject);
        auto funAddLine = [&opGrep](const Vector3d& ptStart, const Vector3d& ptEnd)
        {
            OwnerPtr<IGraphicsLine> symbolLine = IGraphicsLine::Create(ptStart, ptEnd);
            opGrep->AddChild(TransferOwnership(symbolLine));
        };

        CleanupTempGraphicsShape();
        SteppedLineStruct stStairLine = m_stStairLine;
        Vector3d ptLast;
        ptLast = m_pickedPoints.back();
        
        AddStairLineItem(stStairLine, pos, ptLast);
        std::list<Vector3d> listPoints = StairLineToPoints(stStairLine, m_vectorView);
        auto& iter = listPoints.begin();
        Vector3d ptStart = *iter;
        iter++;
        while (iter != listPoints.end())
        {
            Vector3d ptEnd = *iter;
            funAddLine(ptStart, ptEnd);
            ptStart = ptEnd;
            iter++;
        }
        funAddLine(ptStart, m_stStairLine.StartPoint);
        DrawTempGraphicsElementShape(TransferOwnership(opGrep));
    }
 
    void startPickItemPointAction()
    {
        OwnerPtr<IPickPointAction> opAction = IPickPointAction::Create(PickPointExchangeData(&m_ptTemp, &m_bUserCancel, &m_pickedPoints, nullptr, nullptr,
            PickPointExchangeData::SnapPlaneType::WorkPlane, false, PickPointExchangeData::PickPointType::TwoDimensionPoint),
            opAction->CreateMoveCallback(&CreateStairLineAction::OnItemPointMoveCallBack, this));        
        m_actionStatus = ActionStatus::PICK_ITEM_POINT;
        StartChildAction(TransferOwnership(opAction));                                                            
    }

    void processFirstStartStatus()
    {
        CleanupTempGraphicsShape();
        if (m_bUserCancel)
        {
            MarkFinishStatus(ActionFinishStatus::Cancelled);
        }
        else
        {
            m_stStairLine.StartPoint = m_ptTemp;
            startPickDirEndAction();
        }
    }
    void processFirstEndStatus()
    {
        CleanupTempGraphicsShape();
        if (m_bUserCancel)
        {
            MarkFinishStatus(ActionFinishStatus::Cancelled);
        }
        else
        {
            m_stStairLine.SectionDirection = m_ptTemp - m_stStairLine.StartPoint;
            m_stStairLine.SectionDirection.Normalize();
            m_vectorVertical = Vector3dUtils::RotateVectorAroundAxis(m_stStairLine.SectionDirection, m_vectorView, Constants::MATH_PI_2);
            m_stStairLine.StepDirection = m_vectorVertical;
            //m_stStairLine.dFirstLenght = Vector3dUtils::Distance(m_ptTemp, m_stStairLine.ptStart);
            //m_stStairLine.dFirstDeep = 3000;
            SteppedLineItem item;
            item.Length = Vector3dUtils::Distance(m_ptTemp, m_stStairLine.StartPoint);
            item.Deep = 3000;
            item.Height = 0.0;
            m_stStairLine.ArrItem.push_back(item);
            startPickItemPointAction();
            
        }
    }
    void processItemPointStatus()
    {
        CleanupTempGraphicsShape();
        if (m_bUserCancel)
        {
            CreateStairView();
            MarkFinishStatus(ActionFinishStatus::Successful);
            
        }
        else
        {
            
            auto iter = m_pickedPoints.end();
            Vector3d ptLast;

            iter--;
            iter--;
            ptLast = *iter;
            AddStairLineItem(m_stStairLine, m_ptTemp, ptLast);
            
            startPickItemPointAction();
        }
    }
    private:
    void AddStairLineItem(SteppedLineStruct& stStairLine,const Vector3d& pointAdd,const Vector3d& pointLast)
    {
        Vector3d vectorTo = pointAdd - pointLast;
        double dDis = Vector3dUtils::Distance(vectorTo, Vector3d());
        vectorTo.Normalize();

        double dAngle = Vector3dUtils::GetAngle(stStairLine.SectionDirection, vectorTo, m_vectorView);
        SteppedLineItem item;
        item.Deep = 5000;
        item.Length = dDis * cos(dAngle);
        item.Height = dDis * sin(dAngle);
        stStairLine.ArrItem.push_back(item);
    };
    void CreateStairView()
    {
        std::wstring strError;
        if (!ICompositeSectionFactory::Get()->SteppedLineIsVaild(m_stStairLine, &strError))
        {
            UiCommonDialog::ShowMessageBox(GBMP_TR(L"提示"), GBMP_TR(strError), (int)UiCommonDialog::ButtonType::OK);
            return;
        }
        //IModelView* pModelView = pCurrentView->GetModelView();
        DBG_WARN_AND_RETURN_VOID_UNLESS(m_pModelView, L"m_pModelView为空",L"GDMPLab",L"2024-03-30");
        IDocument* pDoc = UiDocumentViewUtils::GetCurrentDocument();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

        OwnerPtr<IUserTransaction> opTransaction = IUserTransaction::Create(pDoc, GBMP_TR(L"创建阶梯视图"), true);
        DBG_WARN_AND_RETURN_VOID_UNLESS(opTransaction != nullptr,  L"事务创建不成功",L"GDMPLab",L"2024-03-30");

        GbmpModelViewInput input;
        input.Document = pDoc;

        Coordinate3d coordStairView = GetStairViewCoord(m_pModelView);
        input.Origin = coordStairView.GetOrigin();
        input.RightDirection = coordStairView.X();
        input.UpDirection = coordStairView.Y();
        input.ViewDirection = -coordStairView.Z();
        input.ViewType = SectionViewType;

        std::wstring viewName;
        bool bOk = GbmpModelViewUtil::GetNextModelViewName(pDoc, SectionViewType, L"阶梯面", L" ", viewName);
        DBG_WARN_AND_RETURN_VOID_UNLESS(bOk, L"GetNextModelViewName失败",L"GDMPLab",L"2024-03-30");
        input.Name = viewName;
        input.HasRectViewport = true;
        std::tie(input.RectViewportWidth, input.RectViewportHeight) = GetViewportSize();

        //OwnerPtr<IGraphicsStairPlane> opStairLine = IGraphicsStairPlane::Create(m_stStairLine);
        Coordinate3d coordModelView(m_pModelView->GetOrigin(),m_pModelView->GetRightDirection(),m_pModelView->GetUpDirection());
        IGbmpSteppedViewExternal* pNewSectionView = IGbmpSteppedViewExternal::Create(input,m_pModelView->GetElementId(), m_stStairLine);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pNewSectionView,  L"pNewSectionView为空",L"GDMPLab",L"2024-03-30");
        opTransaction->Commit();

        // 选中刚刚绘制的剖面，显示其范围控制夹点
        UiDocumentViewUtils::GetCurrentUiDocument()->UpdateView();
        OwnerPtr<IGraphicsNodeReference> opGnodeRef = IGraphicsNodeReference::CreateGraphicsElementShapeReference(pNewSectionView->GetViewSymbolId());
        ISelection::Get()->Clear(UiDocumentViewUtils::GetCurrentDocument());
        ISelection::Get()->AddGraphicsNodeReference(UiDocumentViewUtils::GetCurrentDocument(), *opGnodeRef);

        IHighlights::Get()->Clear();
        std::vector<OwnerPtr<IGraphicsNodeReference>> opGnodeRefs;
        opGnodeRefs.push_back(TransferOwnership(opGnodeRef));
        IHighlights::Get()->AddGraphicsNodeReferences(opGnodeRefs);
    }
    Vector3d GetOriginPoint(const SteppedLineStruct& steppedLine)
    {
        double dLength = 0.0;
        double dMixHeight = 0.0;
        double dMaxHeight = 0.0;
        std::tie(dLength, dMixHeight, dMaxHeight) = GetHeightRange(m_stStairLine.ArrItem);
        Vector3d pointOrigin = m_stStairLine.StartPoint 
            + m_stStairLine.SectionDirection * (dLength / 2.0)
            + m_stStairLine.StepDirection*dMixHeight;
        return pointOrigin;
    }
    std::tuple<double,double,double> GetHeightRange(const std::vector<SteppedLineItem>& arrItem)
    {
        double dLength = 0.0;
        for (auto& item : arrItem)
        {
            dLength += item.Length;
        }

        double dHeightCurrent = arrItem[0].Height;
        double heightMin = dHeightCurrent;
        double heightMax = dHeightCurrent + arrItem[0].Deep;
        for (size_t i = 1; i < arrItem.size(); i++)
        {
            dHeightCurrent += arrItem[i].Height;
            double dHeightDeep = dHeightCurrent + arrItem[i].Deep;
            if (heightMin > dHeightCurrent)
            {
                heightMin = dHeightCurrent;
            }
            if (heightMax < dHeightDeep)
            {
                heightMax = dHeightDeep;
            }
        }
        return { dLength,heightMin,heightMax };
        //hHeight = heightMax - heightMin;
    }
    Coordinate3d GetStairViewCoord(const IModelView* pModelView)
    {
        Vector3d zModelView = -pModelView->GetViewDirection();
        Vector3d xStairView = m_stStairLine.SectionDirection;
        Vector3d yStairView = zModelView;
        
        
        return Coordinate3d(GetOriginPoint(m_stStairLine), xStairView, yStairView);
    }
    
    std::tuple<double, double> GetViewportSize()
    {
        double dLenght = 0.0;
        //double dWidht = 0.0;
        for (auto& item : m_stStairLine.ArrItem)
        {
            dLenght += item.Length;
            //dWidht += item.dDeep;
        }
        return { dLenght,30000 };
    }
};

//////////////////////////////////////////////////////////////////////////
class CmdCreateSteppedView : public GbmpCommandBase
{
public:
    CmdCreateSteppedView();

    virtual bool IsEnabled() const override;
    virtual bool IsVisible() const override;

    virtual OwnerPtr<IAction> ExecuteCommand(const CommandParameters& cmdParams) override;

private:
    const std::wstring OnActionPromptMsg(DrawLineStatus drawLineStatus);
    OwnerPtr<IGraphicsNodeGroup> OnLineUpdate(IUiView* pCurrentView, const Vector3d& startPt, const Vector3d& endPt);
    ElementId OnLineFinished(IUiView* pCurrentView, const Vector3d& startPt, const Vector3d& endPt, const ISnap* pStartSnap, const ISnap* pEndSnap);

};

//////////////////////////////////////////////////////////////////////////
REGISTER_GM_COMMAND(CmdCreateSteppedView);

CmdCreateSteppedView::CmdCreateSteppedView()
    : GbmpCommandBase(ID_CMD_GBMP_CREATE_STAIR_VIEW)
{
}

bool CmdCreateSteppedView::IsEnabled() const
{
    CMD_ONLY_ENABLE_IN_PROJECT_ENVIRONMENT
}

bool CmdCreateSteppedView::IsVisible() const
{
    CMD_ONLY_VISIBLE_IN_PROJECT_ENVIRONMENT
}

OwnerPtr<IAction> CmdCreateSteppedView::ExecuteCommand(const CommandParameters& cmdParams)
{
    // 只支持在平面视图（标高视图和天花板视图）中启动Action
    IModelView* pModelView = UiDocumentViewUtils::GetCurrentModelView();
    if (pModelView->GetUserDataId() != PlanViewType && pModelView->GetUserDataId() != ElevationViewType)

    {
        UiCommonDialog::ShowMessageBox(GBMP_TR(L"创建剖面视图"), GBMP_TR(L"暂时仅允许在平面视图和立面视图中执行该命令"), (int)UiCommonDialog::ButtonType::OK);
        return nullptr;
    }

    return NEW_AS_OWNER_PTR(CreateStairLineAction);
}
